package edu.indiana.d2i.vmm.cloud;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.indiana.d2i.vmm.SigiriHTRCConfiguration;
import edu.indiana.d2i.vmm.job.SigiriHTRCConstants;

// single thread!
public class OpenStackCloud implements CloudProvider {
	private String euca2oolPath, keyname;
	private final int ITEMS_IN_LINE = 11;
	private final int MAX_IPASSIGN_TRY = 3;
	// move this to image management
	private String user;
	private String pwd;
	private String workDir;

	private static final Log logger = LogFactory.getLog(OpenStackCloud.class);

	public OpenStackCloud(SigiriHTRCConfiguration conf) {
		euca2oolPath = conf.getProperty(SigiriHTRCConstants.VM_EUCA2OOLS_HOME);
		keyname = conf.getProperty(SigiriHTRCConstants.VM_AUTH_KEY);
		user = conf.getProperty(SigiriHTRCConstants.VM_USER_NAME);
		pwd = conf.getProperty(SigiriHTRCConstants.VM_USER_PWD);
		workDir = conf.getProperty(SigiriHTRCConstants.VM_WORK_DIR);
	}

	@Override
	public List<CloudInstanceInfo> requestVMs(CloudVMRequest request)
			throws Exception {
		// start a shell		
		ProcessBuilder pb = new ProcessBuilder("/bin/bash");
		pb.directory(new File("/bin"));
		pb.redirectErrorStream(true);
		Process process = pb.start();

		// run commands
		BufferedReader results = new BufferedReader(new InputStreamReader(process.getInputStream()));
		PrintWriter console = new PrintWriter(new BufferedWriter(
				new OutputStreamWriter(process.getOutputStream())), true);
		console.println("cd " + euca2oolPath);
		console.println("source " + "novarc");

		// create VMs			
		String screen;
		List<CloudInstanceInfo> vms = new ArrayList<CloudInstanceInfo>();
		logger.info("euca-run-instances " + 
				String.format("-k %s %s -t %s -n %s", keyname, request.imageId,
				request.imageType,
				String.valueOf(request.instanceNumber)));
		console.println("euca-run-instances " + 
				String.format("-k %s %s -t %s -n %s", keyname, request.imageId,
				request.imageType,
				String.valueOf(request.instanceNumber)));
		screen = results.readLine();
		if (!screen.contains("RESERVATION")) { // regular expression!!!
			logger.error(screen);
			return null;
		}
		int lineNum = 0;
		while (lineNum < request.instanceNumber) {
			screen = results.readLine();
			String[] items = screen.split("\\s+");	
			CloudInstanceInfo vm = new CloudInstanceInfo();
			vm.setInstanceID(items[1]).setImageID(items[2]);
			vms.add(vm);
			logger.info("create an instance " + vm.instanceID);
			lineNum++;
		}
		Thread.sleep(5000);
		
		// assign public IP to every VM
		for (CloudInstanceInfo cloudInstanceInfo : vms) {
			logger.info("euca-associate-address -i " + cloudInstanceInfo.instanceID + 
			" `euca-describe-addresses |grep -v i-|cut -f2|sort|head -n1`");
			console.println("euca-associate-address -i " + cloudInstanceInfo.instanceID + 
					" `euca-describe-addresses |grep -v i-|cut -f2|sort|head -n1`");
			screen = results.readLine();
			int count = 0;
			do {
				// change to regular exp!!!
				if (screen.contains("ADDRESS") && screen.contains(cloudInstanceInfo.instanceID)) {
					break;
				} else {
					logger.error(screen);
					count++;
					Thread.sleep(5000);
				}
			} while (count < MAX_IPASSIGN_TRY);
			if (count >= MAX_IPASSIGN_TRY) {
				logger.error("Unable to associate public IP to instance " + cloudInstanceInfo.instanceID);
				shutdownVMs(vms);
				return null;
			}
		}
		
		// set VM IP info
		for (CloudInstanceInfo cloudVMInfo : vms) {
			console.println("euca-describe-instances | grep " + cloudVMInfo.instanceID);
			screen = results.readLine();
//			if () {} // regular expression check!!! 
			
			String[] items = screen.split("\\s+");
			if (items.length != ITEMS_IN_LINE) {
				logger.error("Unable to get VM IP information, error info from OpenStack: " + screen);
				shutdownVMs(vms);
				return null;
			}
			cloudVMInfo.setPublicIP(items[3]).setPrivateIP(items[4]).setVmType(items[8]).
				setUser(user).setPwd(pwd).setWorkDir(workDir);
		}
		console.println("exit");

		return vms;
	}

	@Override
	public void shutdownVMs(List<CloudInstanceInfo> instances) throws Exception {
		// start a shell		
		ProcessBuilder pb = new ProcessBuilder("/bin/bash");
		pb.directory(new File("/bin"));
		pb.redirectErrorStream(true);
		Process process = pb.start();
		
		// run commands
		PrintWriter console = new PrintWriter(new BufferedWriter(
				new OutputStreamWriter(process.getOutputStream())), true);
		console.println("cd " + euca2oolPath);
		console.println("source " + "novarc");
		
		for (CloudInstanceInfo cloudVMInfo : instances) {
			logger.info("euca-terminate-instances " + cloudVMInfo.instanceID);
			console.println("euca-terminate-instances " + cloudVMInfo.instanceID);
		}
	}
}
